#region using...

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Threading;

using Infragistics.Windows.Controls;
using Infragistics.Windows.DataPresenter;
using Infragistics.Windows.DataPresenter.Events;

using ViewOnCore;
using ViewOnCore.Helper.WPF;
using ViewOnCore.Helper.Process;
using ViewOnCore.Helper.Debug;
using ViewOnCore.Helper.Collection;
using ViewOnCore.HostContract;
using ViewOnCore.HostContract.Media;
using ViewOnCore.Device;
using ViewOnCore.Visual;

using UI.Control;
using MusicPlayerAddIn.Properties;

#endregion

namespace MusicPlayerAddIn
{

    /// <summary>
    /// This Add-in is used to display the "music player" in mode "Media Player".
    /// </summary>
    public partial class MusicPlayer : VisualPart
    {

        #region Variables

        //---- Lists
        private CollectionViewSource _sourceCVS;

        private List<string> _sourceGenresOC = new List<string>();
        private CollectionViewSource _sourceGenresCVS;

        private List<string> _sourceArtistsOC = new List<string>();
        private CollectionViewSource _sourceArtistsCVS;

        private List<string> _sourceAlbumsOC = new List<string>();
        private CollectionViewSource _sourceAlbumsCVS;

        //---- Story boards
        private Storyboard _importButtonShowStoryboard;
        private Storyboard _importButtonHideStoryboard;

        //----
        // This variable is used because a modification to a CVS call some events.
        // Setting this variable to true tell that we have not to handle it because
        // it is a special case.
        private bool _lockEventsPropagation = false;
        private bool _refreshMusicView;

        //----
        private Music _currentMusic;
        private Random _shuffleRandom = new Random();

        //---- Reset the data binding after the sliding ?
        private MediaFeature _previousFeature;
        private MediaItem _previousFeatureItem;

        //---- Drag & drop
        private XamDataGridDragManager<MediaData> _dragManager;

        //---- Context menu
        private MenuItem _openFolderMenuItem;
        private MenuItem _copyToMenuItem;
        private MenuItem _burnMenuItem;

        #endregion

        #region Constructor

        public MusicPlayer()
        {
            InitializeComponent();

            MediaTypeTags.Add("Music");

            //---- Display details view
            gvMusic.Visibility = Visibility.Hidden;
            griFilters.Visibility = Visibility.Visible;

            //---- Set up the main grid
            _sourceCVS = (CollectionViewSource)FindResource("MusicViewContentDataSource");

            //---- Set up other grids
            _sourceGenresCVS = (CollectionViewSource)FindResource("MusicViewGenresDataSource");
            _sourceArtistsCVS = (CollectionViewSource)FindResource("MusicViewArtistsDataSource");
            _sourceAlbumsCVS = (CollectionViewSource)FindResource("MusicViewAlbumsDataSource");

            //---- Retreive the storyboards
            _importButtonShowStoryboard = (Storyboard)FindResource("importButtonShowStoryboard");
            _importButtonHideStoryboard = (Storyboard)FindResource("importButtonHideStoryboard");

            //---- Prepare the views
            _sourceGenresCVS.Source = _sourceGenresOC;
            _sourceArtistsCVS.Source = _sourceArtistsOC;
            _sourceAlbumsCVS.Source = _sourceAlbumsOC;

            //---- Sorting
            ((ListCollectionView)_sourceGenresCVS.View).CustomSort = new FilterSortDescription();
            ((ListCollectionView)_sourceArtistsCVS.View).CustomSort = new FilterSortDescription();
            ((ListCollectionView)_sourceAlbumsCVS.View).CustomSort = new FilterSortDescription();

            //---- Media service
            Host.MediaService.MediaDataOperation += new MediaDataOperationEventHandler(MediaService_MediasOperation);
            Host.MediaService.MediaSelectionChanged += new EventHandler(MediaService_MediaSelectionChanged);
            Host.MediaService.MediaDeviceActivation += new EventHandler(MediaService_MediaDeviceActivation);

            //---- Host
            Host.HostCommand += new HostCommandEventHandler(Host_HostCommand);

            //---- Plugable behaviors
            Host.MediaService.RequestCurrentMedia = this.RequestCurrentMusic;
            Host.MediaService.RequestNextMedia = this.RequestNextMusic;
            Host.MediaService.RequestPreviousMedia = this.RequestPreviousMusic;

            //---- Drag & drop
            //_dragManager = new XamDataGridDragManager<MediaData>(xdgMedias);
            //xdgMedias.AllowDrop = true;
            //xdgMedias.Drop += new DragEventHandler(xdgMedias_Drop);
            //xdgMedias.DragOver += new DragEventHandler(xdgMedias_DragOver);

            //---- List view events
            xdgMedias.MouseDoubleClick += new MouseButtonEventHandler(xdgMedias_MouseDoubleClick);
            xdgMedias.PreviewKeyUp += new KeyEventHandler(xdgMedias_KeyUp);
            xdgMedias.SelectedCellsChanged += new Microsoft.Windows.Controls.SelectedCellsChangedEventHandler(xdgMedias_SelectedCellsChanged); // SelectedItemsChanged += new EventHandler<Infragistics.Windows.DataPresenter.Events.SelectedItemsChangedEventArgs>(xdgMedias_SelectedItemsChanged);
            xdgMedias.PreviewMouseRightButtonDown += new MouseButtonEventHandler(xdgMedias_PreviewMouseRightButtonDown);

            // Allows manual management of the deletion
            xdgMedias.CanUserDeleteRows = false;
            xdgMedias.CanUserAddRows = false;

            //---- Graphical view
            gvMusic.SelectionChanged += new RoutedEventHandler(gvMusic_ImageSelected);

            //---- Tags cloud
            tcFilter.SelectionChanged += new SelectionChangedEventHandler(tcFilter_SelectionChanged);

            //----
            btnExpand.Click += new RoutedEventHandler(btnExpand_Click);

            //---- Toolbar
            btnBurn.IsEnabled = Host.BurningService.HasBurningDevice;
            btnBurn.Click += new RoutedEventHandler(btnBurn_Click);

            btnRemove.IsEnabled = false;
            btnRemove.Click += new RoutedEventHandler(btnRemove_Click);

            btnCopyTo.IsEnabled = false;
            btnCopyTo.Click += new RoutedEventHandler(btnCopyTo_Click);

            btnImport.Click += new RoutedEventHandler(btnImport_Click);

            txtSearch.KeyDown += new KeyEventHandler(txtSearch_KeyDown);
            btnSearch.Click += new RoutedEventHandler(btnSearch_Click);

            rabViewDetails.IsChecked = true;
            rabViewDetails.Checked += new RoutedEventHandler(rabViewDetails_Checked);
            rabViewTagsCloud.Checked += new RoutedEventHandler(rabViewTagsCloud_Checked);
            rabViewCoverFlow.Checked += new RoutedEventHandler(rabViewCoverFlow_Checked);
            rabViewKiosk.Checked += new RoutedEventHandler(rabViewKiosk_Checked);

            //---- Context menu
            ContextMenu menu = (ContextMenu)FindResource("MusicDetailsViewContextMenuSingle");
            ((MenuItem)menu.Items[0]).Click += new RoutedEventHandler(contextMenuPlay_Click);
            ((MenuItem)menu.Items[1]).Click += new RoutedEventHandler(contextMenuRemove_Click);

            menu = (ContextMenu)FindResource("MusicDetailsViewContextMenuMultiple");
            ((MenuItem)menu.Items[0]).Click += new RoutedEventHandler(contextMenuRemove_Click);

            _openFolderMenuItem = new MenuItem();
            _openFolderMenuItem.Header = Globalization.MusicPart_ContextMenu_OpenFile;
            _openFolderMenuItem.Click += new RoutedEventHandler(contextMenuOpenDirectory_Click);

            _copyToMenuItem = new MenuItem();
            _copyToMenuItem.Header = Globalization.MusicPart_ContextMenu_CopyTo;
            _copyToMenuItem.Click += new RoutedEventHandler(contextMenuCopyToItem_Click);

            _burnMenuItem = new MenuItem();
            _burnMenuItem.Header = Globalization.MusicPart_ContextMenu_BurnCD;
            _burnMenuItem.Click += new RoutedEventHandler(contextMenuBurn_Click);
        }

        #endregion

        #region VisualPart : Properties

        override public string Handle
        {
            get
            {
                return "ViewOnTv_MusicPlayerAddIn";
            }
        }

        #endregion

        #region VisualPart : OnRequestVisualAddIn

        override public VisualPartPriority OnRequestVisualPartPriority()
        {
            if (Host.VisualService.VisualPartMode != VisualPartMode.Normal)
                return VisualPartPriority.None;

            if (Host.MediaService.CurrentMediaDeviceFeature.MediaTypeTags.Contains("Music"))
                return VisualPartPriority.Normal;

            return VisualPartPriority.None;
        }

        #endregion

        #region VisualPart : OnRegistered/OnUnregistered

        virtual public void OnRegistered()
        { }

        virtual public void OnUnregistered()
        {
        }

        #endregion

        #region VisualPart : OnPreDisplay/OnPostDisplay/OnPreHidde

        private bool _isDisplayed;

        override public void OnPreDisplay(Control container)
        {
        }

        override public void OnPostDisplay(Control container)
        {
            _isDisplayed = true;

            //---- Set the dispatcher for the covers manager
            AlbumCoverManager.Dispatcher = this.Dispatcher;

            //---- Reset the databinding after the sliding or not ?
            if (ViewOnCore.Visual.VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                if (_refreshMusicView)
                {
                    _refreshMusicView = false;

                    // Prepare it
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate
                        {
                            gvMusic.ItemsSource = AlbumCoverManager.Items;
                            kiosk.DataSource = AlbumCoverManager.Items;
                            UpdateMusicView();
                            AlbumCoverManager.Update();
                        });
                }
        }

        override public void OnPreHide(Control container)
        {
            _isDisplayed = false;
        }

        #endregion

        #region Drop file on list

        void xdgMedias_DragOver(object sender, DragEventArgs e)
        {
            string[] filePathInfo = e.Data.GetData("FileDrop", true) as string[];
            if (filePathInfo == null)
            {
                e.Handled = true;
                e.Effects = DragDropEffects.None;
                return;
            }

            foreach (string filePath in filePathInfo)
                if (!Host.MediaService.IsSupportedMusicFile(filePath))
                {
                    e.Handled = true;
                    e.Effects = DragDropEffects.None;
                    return;
                }

            e.Effects = DragDropEffects.Copy;
        }

        void xdgMedias_Drop(object sender, DragEventArgs e)
        {
            string[] filePathInfo = e.Data.GetData("FileDrop", true) as string[];
            if (filePathInfo == null)
                return;

            bool playDefaultMedia = false;

            foreach (string filePath in filePathInfo)
            {
                if (!Host.MediaService.IsSupportedMusicFile(filePath))
                    continue;

                //---- Import the file
                Music media = Music.FromFile(filePath);

                if (Host.MediaService.CurrentMediaDeviceFeature.CanImport(null, media))
                    Host.MediaService.CurrentMediaDeviceFeature.AddMediaData(null, media, false);

                //---- Play the file
                if (!playDefaultMedia)
                {
                    playDefaultMedia = true;
                    Host.MediaService.PlayMedia(media);
                }
            }

            //---- Save the changes
            Host.MediaService.CurrentMediaDevice.OnSaveChanges();
            Host.MediaService.NotifyMediaDataOperation(Host.MediaService.CurrentMediaDeviceFeature, null, MediaOperationKind.Imported, null);
        }

        #endregion

        #region Tag panel : Expand / Collapse

        private bool _isExpanded = true;
        private double collapseHeight = -1;

        void btnExpand_Click(object sender, RoutedEventArgs e)
        {
            if (_isExpanded)
                Collapse();
            else
                Expand();

            _isExpanded = !_isExpanded;
        }

        void Collapse()
        {
            //if (collapseHeight < 0)
            collapseHeight = griMain.RowDefinitions[5].ActualHeight;

            GridLengthAnimation gla = new GridLengthAnimation(GridUnitType.Pixel);
            gla.From = new GridLength(collapseHeight, GridUnitType.Pixel);
            gla.To = new GridLength(18, GridUnitType.Pixel); ;
            gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
            griMain.RowDefinitions[5].BeginAnimation(RowDefinition.HeightProperty, gla);

            imgCollapse.Visibility = Visibility.Collapsed;
            imgExpand.Visibility = Visibility.Visible;
        }

        void Expand()
        {
            GridLengthAnimation gla = new GridLengthAnimation(GridUnitType.Pixel);
            gla.From = new GridLength(18, GridUnitType.Pixel);
            gla.To = new GridLength(collapseHeight, GridUnitType.Pixel); ;
            gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
            griMain.RowDefinitions[5].BeginAnimation(RowDefinition.HeightProperty, gla);

            imgExpand.Visibility = Visibility.Collapsed;
            imgCollapse.Visibility = Visibility.Visible;
        }

        #endregion

        #region Switch the view : Cover flow / Kiosk / Tags cloud / Filters

        void rabViewCoverFlow_Checked(object sender, RoutedEventArgs e)
        {
            // Reset the "Genre" & "Artist" filters
            lstGenre.SelectedIndex = 0;
            lstAlbum.SelectedIndex = 0;
            lstArtist.SelectedIndex = 0;

            gvMusic.Visibility = Visibility.Visible;
            AlbumCoverManager.Reload();

            gsSplitter.Visibility = Visibility.Visible;
            griFilters.Visibility = Visibility.Hidden;
            xdgMedias.Visibility = Visibility.Visible;
            kiosk.Visibility = Visibility.Hidden;
            griFilters.Visibility = Visibility.Hidden;
            tcFilter.Visibility = Visibility.Hidden;
            griMusicInfo.Visibility = Visibility.Visible;

            ShowControl(btnBurn);
            ShowControl(btnCopyTo);
            ShowControl(btnImport);
            ShowControl(btnRemove);
            ShowControl(txtSearch);
            ShowControl(btnSearch);
        }

        void rabViewTagsCloud_Checked(object sender, RoutedEventArgs e)
        {
            // Reset the "Genre" & "Artist" filters
            lstGenre.SelectedIndex = 0;
            lstAlbum.SelectedIndex = 0;
            lstArtist.SelectedIndex = 0;

            // Change the view
            gsSplitter.Visibility = Visibility.Visible;
            griFilters.Visibility = Visibility.Hidden;
            xdgMedias.Visibility = Visibility.Visible;
            kiosk.Visibility = Visibility.Hidden;
            gvMusic.Visibility = Visibility.Hidden;
            griFilters.Visibility = Visibility.Hidden;
            tcFilter.Visibility = Visibility.Visible;
            griMusicInfo.Visibility = Visibility.Visible;

            ShowControl(btnBurn);
            ShowControl(btnCopyTo);
            ShowControl(btnImport);
            ShowControl(btnRemove);
            ShowControl(txtSearch);
            ShowControl(btnSearch);

            //EnumerableWrapperForInfragisticsBugFix igCollection = _sourceCVS.Source as EnumerableWrapperForInfragisticsBugFix;
            //MusicTagsCloudManager.Refresh(tcFilter, igCollection.Enumerable as ObservableCollection<MediaData>);
            MusicTagsCloudManager.Refresh(tcFilter, (ExtObservableCollection<MediaData>)_sourceCVS.Source);
        }

        void rabViewDetails_Checked(object sender, RoutedEventArgs e)
        {
            gsSplitter.Visibility = Visibility.Visible;
            griFilters.Visibility = Visibility.Visible;
            xdgMedias.Visibility = Visibility.Visible;
            kiosk.Visibility = Visibility.Hidden;
            //gvMusic.ChangeView(GraphicalViewType.None);
            gvMusic.Visibility = Visibility.Hidden;
            griFilters.Visibility = Visibility.Visible;
            tcFilter.Visibility = Visibility.Hidden;
            griMusicInfo.Visibility = Visibility.Visible;

            ShowControl(btnBurn);
            ShowControl(btnCopyTo);
            ShowControl(btnImport);
            ShowControl(btnRemove);
            ShowControl(txtSearch);
            ShowControl(btnSearch);
        }

        void rabViewKiosk_Checked(object sender, RoutedEventArgs e)
        {
            // Reset the "Genre" & "Artist" filters
            lstGenre.SelectedIndex = 0;
            lstAlbum.SelectedIndex = 0;
            lstArtist.SelectedIndex = 0;

            // Change the view
            //gvMusic.ChangeView(GraphicalViewType.None);
            gvMusic.Visibility = Visibility.Hidden;

            gsSplitter.Visibility = Visibility.Hidden;

            griFilters.Visibility = Visibility.Hidden;
            xdgMedias.Visibility = Visibility.Hidden;

            griFilters.Visibility = Visibility.Hidden;
            tcFilter.Visibility = Visibility.Hidden;

            AlbumCoverManager.Reload();

            kiosk.Visibility = Visibility.Visible;
            griMusicInfo.Visibility = Visibility.Hidden;

            HideControl(btnBurn);
            HideControl(btnCopyTo);
            HideControl(btnImport);
            HideControl(btnRemove);
            HideControl(txtSearch);
            HideControl(btnSearch);
        }

        #endregion

        #region Cover flow

        void gvMusic_ImageSelected(object sender, RoutedEventArgs args)
        {
            Music music = null;

            if (gvMusic.SelectedIndex > -1)
                music = AlbumCoverManager.Items[gvMusic.SelectedIndex].Tag as Music;

            if (music == null)
            {
                lstAlbum.SelectedIndex = 0;
                lstArtist.SelectedIndex = 0;
                return;
            }

            SetMusicFilter(music.Album, music.Artist, null);
        }

        #endregion

        #region Tags Cloud

        void tcFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            lstGenre.SelectedIndex = 0;
            lstAlbum.SelectedIndex = 0;

            if (e.AddedItems.Count < 1)
            {
                lstArtist.SelectedIndex = 0;
                return;
            }

            System.Windows.Data.CollectionViewGroup item = e.AddedItems[0] as System.Windows.Data.CollectionViewGroup;
            SetMusicFilter(null, (string)item.Name, null);
        }

        #endregion

        #region PrepareFilters

        private void PrepareFilters(ExtObservableCollection<MediaData> medias,
                                    out SortedList<string, string> filterMusicGenres,
                                    out SortedList<string, string> filterMusicAlbums,
                                    out SortedList<string, string> filterMusicArtists)
        {
            filterMusicGenres = new SortedList<string, string>();
            filterMusicAlbums = new SortedList<string, string>();
            filterMusicArtists = new SortedList<string, string>();

            for (int index = 0; index < medias.Count; index++)
            {
                Music music = medias[index] as Music;

                if (music.Album.Length > 0 && !filterMusicAlbums.ContainsKey(music.Album))
                    filterMusicAlbums.Add(music.Album, music.Album);

                if (music.Artist.Length > 0 && !filterMusicArtists.ContainsKey(music.Artist))
                    filterMusicArtists.Add(music.Artist, music.Artist);

                if (music.Genre.Length > 0 && !filterMusicGenres.ContainsKey(music.Genre))
                    filterMusicGenres.Add(music.Genre, music.Genre);
            }
        }

        #endregion

        #region UpdateMusicView

        private void UpdateMusicView()
        {
            if (Host.MediaService.CurrentMediaDeviceFeature.IsMediaDatasLoaded)
            {
                AsyncUpdateMusicView();
                return;
            }

            //--- Async Load
            spinner.Start();

            Thread thread = new Thread(new ThreadStart(AsyncUpdateMusicViewPreload));
            thread.Priority = ThreadPriority.Lowest;
            thread.Start();
        }

        private void AsyncUpdateMusicViewPreload()
        {
            // Force loading
            object o = Host.MediaService.CurrentMediaDeviceFeature.MediaDatas;

            while (!Host.MediaService.CurrentMediaDeviceFeature.IsMediaDatasLoaded)
                Thread.Sleep(200);

            // Stop spinning and update
            Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (DelegateInvoker)delegate
            {
                spinner.Stop();
                AsyncUpdateMusicView();
            });
        }

        private void AsyncUpdateMusicView()
        {
            ExtObservableCollection<MediaData> medias;
            if (Host.MediaService.CurrentMediaDeviceFeatureItem == null)
                medias = Host.MediaService.CurrentMediaDeviceFeature.MediaDatas;
            else
                medias = Host.MediaService.CurrentMediaDeviceFeatureItem.MediaDatas;

            //---- Refresh the filter
            SortedList<string, string> filterMusicGenres;
            SortedList<string, string> filterMusicAlbums;
            SortedList<string, string> filterMusicArtists;
            PrepareFilters(medias, out filterMusicGenres, out filterMusicAlbums, out filterMusicArtists);

            //---- Async refresh
            if (Dispatcher.CheckAccess())
                AsyncUpdateMusicView(medias, filterMusicGenres, filterMusicAlbums, filterMusicArtists);
            else
                Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                            (DelegateInvoker)delegate { AsyncUpdateMusicView(medias, filterMusicGenres, filterMusicAlbums, filterMusicArtists); });
        }

        #endregion

        #region UpdateMusicView_Removed

        public void UpdateMusicView_Removed(List<MediaData> mediaDatasToRemove)
        {
            ExtObservableCollection<MediaData> medias;
            if (Host.MediaService.CurrentMediaDeviceFeatureItem == null)
                medias = Host.MediaService.CurrentMediaDeviceFeature.MediaDatas;
            else
                medias = Host.MediaService.CurrentMediaDeviceFeatureItem.MediaDatas;

            //---- Refresh the filter
            SortedList<string, string> filterMusicGenres;
            SortedList<string, string> filterMusicAlbums;
            SortedList<string, string> filterMusicArtists;
            PrepareFilters(medias, out filterMusicGenres, out filterMusicAlbums, out filterMusicArtists);

            //---- Async refresh
            if (Dispatcher.CheckAccess())
                AsyncUpdateMusicView(null, filterMusicGenres, filterMusicAlbums, filterMusicArtists);
            else
                Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                            (DelegateInvoker)delegate { AsyncUpdateMusicView(null, filterMusicGenres, filterMusicAlbums, filterMusicArtists); });
        }

        #endregion

        #region UpdateMusicView_Updated

        public void UpdateMusicView_Updated(List<MediaData> mediaDatasToRemove)
        {
            ExtObservableCollection<MediaData> medias;
            if (Host.MediaService.CurrentMediaDeviceFeatureItem == null)
                medias = Host.MediaService.CurrentMediaDeviceFeature.MediaDatas;
            else
                medias = Host.MediaService.CurrentMediaDeviceFeatureItem.MediaDatas;

            //---- Refresh the filter
            SortedList<string, string> filterMusicGenres;
            SortedList<string, string> filterMusicAlbums;
            SortedList<string, string> filterMusicArtists;
            PrepareFilters(medias, out filterMusicGenres, out filterMusicAlbums, out filterMusicArtists);

            //---- Async refresh
            if (Dispatcher.CheckAccess())
                AsyncUpdateMusicViewUpdated(filterMusicGenres, filterMusicAlbums, filterMusicArtists);
            else
                Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                            (DelegateInvoker)delegate { AsyncUpdateMusicViewUpdated(filterMusicGenres, filterMusicAlbums, filterMusicArtists); });
        }

        #endregion

        #region AsyncUpdateMusicView

        private void AsyncUpdateMusicView(ExtObservableCollection<MediaData> medias,
                                            SortedList<string, string> filterMusicGenres,
                                            SortedList<string, string> filterMusicAlbums,
                                            SortedList<string, string> filterMusicArtists)
        {
            using (_sourceCVS.DeferRefresh())
            using (_sourceGenresCVS.DeferRefresh())
            using (_sourceArtistsCVS.DeferRefresh())
            using (_sourceAlbumsCVS.DeferRefresh())
            {
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterGenre);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterAlbum);

                //---- Main list
                if (medias != null)
                    _sourceCVS.Source = medias;

                //---- Reset
                lstGenre.SelectedItem = null;
                lstArtist.SelectedItem = null;
                lstAlbum.SelectedItem = null;

                _sourceGenresOC.Clear();
                _sourceArtistsOC.Clear();
                _sourceAlbumsOC.Clear();

                _sourceGenresOC.Add("*");
                _sourceArtistsOC.Add("*");
                _sourceAlbumsOC.Add("*");

                foreach (string key in filterMusicGenres.Keys)
                    _sourceGenresOC.Add(key);

                foreach (string key in filterMusicArtists.Keys)
                    _sourceArtistsOC.Add(key);

                foreach (string key in filterMusicAlbums.Keys)
                    _sourceAlbumsOC.Add(key);

                lstGenre.ItemsSource = null;
                lstGenre.ItemsSource = _sourceGenresOC;
                lstArtist.ItemsSource = null;
                lstArtist.ItemsSource = _sourceArtistsOC;
                lstAlbum.ItemsSource = null;
                lstAlbum.ItemsSource = _sourceAlbumsOC;

                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterGenre);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterAlbum);
            }

            //---- Reload the data context
            if (gvMusic.Visibility == Visibility.Visible)
            {
                AlbumCoverManager.Reload();

                // Scroll to default
                gvMusic.SelectedIndex = 0;
            }

            if (kiosk.Visibility == Visibility.Visible)
                kiosk.ReloadDataContext();

            //---- Reload the data context
            if (tcFilter.Visibility == Visibility.Visible)
                MusicTagsCloudManager.Refresh(tcFilter, medias);

            //---- Reset the scroll bar to the top
            //if (xdgMedias.Items.Count > 0)
            //    xdgMedias.BringRecordIntoView(xdgMedias.Records[0]);
            //if (medias.Count > 0)
            //    xdgMedias.ScrollIntoView(medias[0]);
        }

        #endregion

        #region AsyncUpdateMusicViewUpdated

        private void AsyncUpdateMusicViewUpdated(
                                            SortedList<string, string> filterMusicGenres,
                                            SortedList<string, string> filterMusicAlbums,
                                            SortedList<string, string> filterMusicArtists)
        {
            using (_sourceCVS.DeferRefresh())
            using (_sourceGenresCVS.DeferRefresh())
            using (_sourceArtistsCVS.DeferRefresh())
            using (_sourceAlbumsCVS.DeferRefresh())
            {
                object selectedGenre = lstGenre.SelectedItem;
                object selectedArtist = lstArtist.SelectedItem;
                object selectedAlbum = lstAlbum.SelectedItem;

                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterGenre);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterAlbum);

                //---- Reset
                lstGenre.SelectedItem = null;
                lstArtist.SelectedItem = null;
                lstAlbum.SelectedItem = null;

                _sourceGenresOC.Clear();
                _sourceArtistsOC.Clear();
                _sourceAlbumsOC.Clear();

                _sourceGenresOC.Add("*");
                _sourceArtistsOC.Add("*");
                _sourceAlbumsOC.Add("*");

                foreach (string key in filterMusicGenres.Keys)
                    _sourceGenresOC.Add(key);

                foreach (string key in filterMusicArtists.Keys)
                    _sourceArtistsOC.Add(key);

                foreach (string key in filterMusicAlbums.Keys)
                    _sourceAlbumsOC.Add(key);

                lstGenre.ItemsSource = null;
                lstGenre.ItemsSource = _sourceGenresOC;
                lstArtist.ItemsSource = null;
                lstArtist.ItemsSource = _sourceArtistsOC;
                lstAlbum.ItemsSource = null;
                lstAlbum.ItemsSource = _sourceAlbumsOC;

                lstGenre.SelectedItem = selectedGenre;
                lstArtist.SelectedItem = selectedArtist;
                lstAlbum.SelectedItem = selectedAlbum;

                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterGenre);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterAlbum);
            }

            //---- Update the data contect
            if (gvMusic.Visibility == Visibility.Visible)
                AlbumCoverManager.Update();

            //---- Reload the data context
            if (tcFilter.Visibility == Visibility.Visible)
            {
                //EnumerableWrapperForInfragisticsBugFix igCollection = _sourceCVS.Source as EnumerableWrapperForInfragisticsBugFix;
                //MusicTagsCloudManager.Refresh(tcFilter, igCollection.Enumerable as ObservableCollection<MediaData>);
                MusicTagsCloudManager.Refresh(tcFilter, (ExtObservableCollection<MediaData>)_sourceCVS.Source);
            }

            ////---- Reset the scroll bar to the top
            ////(((VisualTreeHelper.GetChild(lvMyMusic, 0) as Border).Child) as ScrollViewer).ScrollToVerticalOffset(0);
            //if (lvMyMusic.Items.Count > 0)
            //    lvMyMusic.ScrollIntoView(lvMyMusic.Items[0]);
        }

        #endregion

        #region Filters management

        private void SetMusicFilter(string album, string artist, string genre)
        {
            //using (_sourceCVS.DeferRefresh())
            {
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterAlbum);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterGenre);

                if (album == null || album.Trim().Length < 1)
                {
                    if (lstAlbum.SelectedIndex != 0)
                        lstAlbum.SelectedIndex = 0;
                }
                else if (!album.Equals(lstAlbum.SelectedItem))
                    lstAlbum.SelectedItem = album;

                if (artist == null || artist.Trim().Length < 1)
                {
                    if (lstArtist.SelectedIndex != 0)
                        lstArtist.SelectedIndex = 0;
                }
                else if (!artist.Equals(lstArtist.SelectedItem))
                    lstArtist.SelectedItem = artist;

                if (genre == null || genre.Trim().Length < 1)
                {
                    if (lstArtist.SelectedIndex != 0)
                        lstGenre.SelectedIndex = 0;
                }
                else if (!genre.Equals(lstGenre.SelectedItem))
                    lstGenre.SelectedItem = genre;

                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterAlbum);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterArtist);
                _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterGenre);
            }
        }

        void lstGenre_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_lockEventsPropagation)
                return;

            // Refresh filter
            _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterGenre);
            _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterGenre);
        }

        void lstArtist_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_lockEventsPropagation)
                return;

            // Refresh filter
            _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterArtist);
            _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterArtist);
        }

        void lstAlbum_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_lockEventsPropagation)
                return;

            // Refresh filter
            _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterAlbum);
            _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterAlbum);
        }

        /// <summary>
        /// http://www.beacosta.com/Archive/2006_09_01_bcosta_archive.html
        /// In order for the filter event handlers to work properly without interfering with each other,
        /// I made sure they only set the Accepted property of the FilterEventArgs to false when necessary
        /// to filter an item out. I never set the Accepted property to true, since that might override
        /// another filter's decision to keep an item out of the view.
        /// </summary>

        void _sourceCVS_FilterGenre(object sender, FilterEventArgs e)
        {
            Music music = e.Item as Music;

            //---- Check for genre
            if (lstGenre.SelectedIndex > 0)
            {
                string selectedGenre = lstGenre.SelectedValue.ToString();

                if (!music.Genre.Equals(selectedGenre))
                    e.Accepted = false;
            }
        }

        void _sourceCVS_FilterArtist(object sender, FilterEventArgs e)
        {
            Music music = e.Item as Music;

            //---- Check for genre
            if (lstArtist.SelectedIndex > 0)
            {
                string selectedArtist = lstArtist.SelectedValue.ToString();
                if (!music.Artist.Equals(selectedArtist))
                    e.Accepted = false;
            }
        }

        void _sourceCVS_FilterAlbum(object sender, FilterEventArgs e)
        {
            Music music = e.Item as Music;

            //---- Check for genre
            if (lstAlbum.SelectedIndex > 0)
            {
                string selectedAlbum = lstAlbum.SelectedValue.ToString();
                if (!music.Album.Equals(selectedAlbum))
                    e.Accepted = false;
            }
        }

        #endregion

        #region Search

        void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            StartSearch();
        }

        void txtSearch_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
                StartSearch();
        }

        private void StartSearch()
        {
            // Refresh filter
            _sourceCVS.Filter -= new FilterEventHandler(_sourceCVS_FilterSearch);
            _sourceCVS.Filter += new FilterEventHandler(_sourceCVS_FilterSearch);
        }

        void _sourceCVS_FilterSearch(object sender, FilterEventArgs e)
        {
            Music music = e.Item as Music;

            string text = txtSearch.Text.ToLower().Trim();

            if (text.Length > 0)
                if (!music.Genre.ToLower().Contains(text) &&
                    !music.Title.ToLower().Contains(text) &&
                    !music.Album.ToLower().Contains(text) &&
                    !music.Artist.ToLower().Contains(text))
                    e.Accepted = false;
        }

        #endregion

        #region DeleteSelectedItems

        void btnRemove_Click(object sender, RoutedEventArgs e)
        {
            DeleteSelectedItems();
        }

        private void DeleteSelectedItems()
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            string message = String.Format(Globalization.MusicPart_DeleteSelectedItems_Message1, xdgMedias.SelectedItems.Count);
            string message2 = Globalization.MusicPart_DeleteSelectedItems_Message2;

            QuestionDialog dialog = new QuestionDialog(QuestionDialog.QuestionDialogType.YesNo, message, message2, null, null, 350);
            dialog.ShowDialog(Host.ApplicationDesktop,
                                    new ChildWindowDialogClosedHandler(OnConfirmDeleteSelectedItems));
        }

        private void OnConfirmDeleteSelectedItems(ChildWindowDecorator window)
        {
            //---- Check if it is Ok to delete
            QuestionDialog dialog = window.Parent as QuestionDialog;

            if (!dialog.IsOkYes)
                return;

            //----
            if (Host.MediaService.CurrentMediaDeviceFeatureItem != null)
            {
                if (!Host.MediaService.CurrentMediaDeviceFeatureItem.IsRemoveSupported)
                    return;
            }
            else if (!Host.MediaService.CurrentMediaDeviceFeature.IsRemoveSupported)
                return;

            //---- Create a list with the musics to remove
            List<MediaData> musics = new List<MediaData>();
            foreach (object record in xdgMedias.SelectedItems)
                musics.Add(record as Music);

            Host.MediaService.RemoveMediaDatas(musics, Host.MediaService.CurrentMediaDeviceFeature, Host.MediaService.CurrentMediaDeviceFeatureItem, null);
        }

        #endregion

        #region Events : Double click

        void xdgMedias_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            if (IsMouseOverInvalidZone)
                return;

            Host.MediaService.PlayMedia(xdgMedias.SelectedItems[0] as Music);
        }

        #endregion

        #region Events : Delete

        void xdgMedias_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key != Key.Delete)
                return;

            DeleteSelectedItems();
        }

        #endregion

        #region Events : Grid - SelectionChanged

        void xdgMedias_SelectedCellsChanged(object sender, Microsoft.Windows.Controls.SelectedCellsChangedEventArgs e)
        {
            if (xdgMedias.SelectedItems.Count < 1)
            {
                btnBurn.IsEnabled = false;
                btnRemove.IsEnabled = false;
                btnCopyTo.IsEnabled = false;
                return;
            }

            //---- Update the buttons
            btnBurn.IsEnabled = Host.BurningService.HasBurningDevice;
            btnRemove.IsEnabled = true;
            btnCopyTo.IsEnabled = true;

            //---- Update the tags info
            List<Music> selectedMusics = new List<Music>(xdgMedias.SelectedItems.Count);
            foreach (Music record in xdgMedias.SelectedItems)
                selectedMusics.Add(record);

            musicInfo.Refresh(selectedMusics);
        }

        #endregion

        #region Events : Grid - ContextMenu

        void xdgMedias_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            ContextMenu menu = null;
            int startReset = 0;
            if (xdgMedias.SelectedItems.Count == 1)
            {
                if (Host.MediaService.CurrentMediaDeviceFeature.IsDirectPlaySupported)
                {
                    menu = (ContextMenu)FindResource("MusicDetailsViewContextMenuSingle");
                    startReset = 2;
                }
                else
                {
                    menu = (ContextMenu)FindResource("MusicDetailsViewContextMenuSingleNoPlay");
                    startReset = 1;
                }
            }
            else if (xdgMedias.SelectedItems.Count > 1)
            {
                menu = (ContextMenu)FindResource("MusicDetailsViewContextMenuMultiple");
                startReset = 1;
            }

            //---- Reset the menu
            for (int index = menu.Items.Count - 1; index >= startReset; index--)
                menu.Items.RemoveAt(index);

            //---- Configure the menu
            ConfigureContextMenu(menu);

            menu.IsOpen = true;
            e.Handled = true;
        }

        void ConfigureContextMenu(ContextMenu contextMenu)
        {
            MenuItem item;

            //---- Separator
            contextMenu.Items.Add(new Separator());

            //---- Open file directory
            if (Host.MediaService.CurrentMediaDeviceFeature.IsManageFilePathSupported &&
                xdgMedias.SelectedItems.Count == 1)
                contextMenu.Items.Add(_openFolderMenuItem);

            //---- Copy to
            if (_copyToMenuItem.Parent != null)
                ((ContextMenu)_copyToMenuItem.Parent).Items.Remove(_copyToMenuItem);
            contextMenu.Items.Add(_copyToMenuItem);

            //---- Burn to CD / DVD
            if (Host.MediaService.CurrentMediaDeviceFeature.IsBurningSupported && Host.BurningService.HasBurningDevice)
            {
                if (_burnMenuItem.Parent != null)
                    ((ContextMenu)_burnMenuItem.Parent).Items.Remove(_burnMenuItem);
                contextMenu.Items.Add(_burnMenuItem);
            }
        }

        void contextMenuPlay_Click(object sender, RoutedEventArgs e)
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            Host.MediaService.PlayMedia(xdgMedias.SelectedItems[0] as Music);
        }

        void contextMenuRemove_Click(object sender, RoutedEventArgs e)
        {
            DeleteSelectedItems();
        }

        void contextMenuCopyToItem_Click(object sender, RoutedEventArgs e)
        {
            btnCopyTo_Click(null, null);
        }

        void contextMenuOpenDirectory_Click(object sender, RoutedEventArgs e)
        {
            Music music = xdgMedias.SelectedItems[0] as Music;

            string directory = new System.IO.FileInfo(music.Path).Directory.FullName;

            ViewOnCore.Helper.Process.ProcessHelper.ExploreDirectory(directory);
        }

        void contextMenuBurn_Click(object sender, RoutedEventArgs e)
        {
            BurnSelection();
        }

        #endregion

        #region MediaServices : Operations

        void MediaService_MediasOperation(MediaFeature feature, MediaItem featureItem, MediaOperationKind operationKind, object parameter)
        {
            if (!IsActive || !VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                return;

            if (operationKind == MediaOperationKind.Imported &&
                feature == Host.MediaService.CurrentMediaDeviceFeature)
            {
                UpdateMusicView();
                return;
            }

            if (operationKind == MediaOperationKind.Removed)
            {
                UpdateMusicView_Removed((List<MediaData>)parameter);
                return;
            }

            if (operationKind == MediaOperationKind.Updated)
            {
                UpdateMusicView_Updated((List<MediaData>)parameter);
                return;
            }
        }

        #endregion

        #region MediaService : MediaSelectionChanged

        void MediaService_MediaSelectionChanged(object sender, EventArgs e)
        {
            if (!IsActive || !VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                return;

            Music music = Host.MediaService.CurrentMedia as Music;
            if (music == null)
                return;

            if (Dispatcher.CheckAccess())
                AsyncMediaSelectionChanged();
            else
                Dispatcher.Invoke(DispatcherPriority.Send,
                                        (DelegateInvoker)delegate { AsyncMediaSelectionChanged(); });
        }

        void AsyncMediaSelectionChanged()
        {
            // No selection
            if (Host.MediaService.CurrentMedia == null)
            {
                xdgMedias.SelectedItems.Clear();
                return;
            }

            // Set the current music
            SetCurrentMusic(Host.MediaService.CurrentMedia as Music);

            if (Host.MediaService.CurrentMedia != null)
            {
                xdgMedias.SelectedValue = Host.MediaService.CurrentMedia;
                xdgMedias.ScrollIntoView(Host.MediaService.CurrentMedia);

                //DataRecord selectedRecord = xdgMedias.GetRecordFromDataItem(Host.MediaService.CurrentMedia, false);
                //if (selectedRecord != null)
                //{
                //    if (xdgMedias.Visibility == Visibility.Visible) // TO BYPASS A BUG !!!
                //        xdgMedias.BringRecordIntoView(selectedRecord);

                //    xdgMedias.SelectedItems.Records.Clear();
                //    selectedRecord.IsSelected = true;
                //}
            }
        }

        #endregion

        #region MediaService : MediaDeviceActivation

        void MediaService_MediaDeviceActivation(object sender, EventArgs e)
        {
            if (!IsActive || !VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                return;

            btnBurn.IsEnabled = false;
            btnRemove.IsEnabled = false;
            btnCopyTo.IsEnabled = false;

            //---- Show / Hide buttons
            if (Host.MediaService.CurrentMediaDeviceFeatureItem != null)
            {
                if (Host.MediaService.CurrentMediaDeviceFeatureItem.IsImportSupported)
                    _importButtonShowStoryboard.Begin(this);
                else
                    _importButtonHideStoryboard.Begin(this);
            }
            else
            {
                if (Host.MediaService.CurrentMediaDeviceFeature.IsImportSupported)
                    _importButtonShowStoryboard.Begin(this);
                else
                    _importButtonHideStoryboard.Begin(this);
            }

            //---- We must refresh ?
            if (Host.MediaService.CurrentMediaDeviceFeature != _previousFeature || _previousFeatureItem != Host.MediaService.CurrentMediaDeviceFeatureItem)
            {
                if (_isDisplayed)
                {
                    _refreshMusicView = false;
                    UpdateMusicView();
                }
                else
                    _refreshMusicView = true;
            }

            _previousFeature = Host.MediaService.CurrentMediaDeviceFeature;
            _previousFeatureItem = Host.MediaService.CurrentMediaDeviceFeatureItem;

            //---- Reset the tags database
            musicInfo.UpdateIntellisenseDB();
        }

        #endregion

        #region Show/Hide controls

        public void ShowControl(Control control)
        {
            if (control.Opacity == 1)
                return;

            Storyboard controlShowStoryboard = (Storyboard)FindResource("controlShowStoryboard");
            Storyboard clone = (Storyboard)controlShowStoryboard.Clone();

            (((Storyboard)clone.Children[0]).Children[0]).SetValue(Storyboard.TargetNameProperty, control.Name);

            clone.Begin(control);
        }

        public void HideControl(Control control)
        {
            if (control.Opacity == 0)
                return;

            Storyboard controlHideStoryboard = (Storyboard)FindResource("controlHideStoryboard");
            Storyboard clone = (Storyboard)controlHideStoryboard.Clone();

            (((Storyboard)clone.Children[0]).Children[0]).SetValue(Storyboard.TargetNameProperty, control.Name);

            clone.Begin(control);
        }

        #endregion

        #region Import

        public void btnImport_Click(object sender, RoutedEventArgs args)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.ShowDialog((Desktop)Host.ApplicationDesktop,
                                                new ChildWindowDialogClosedHandler(OnDialogClosed));
        }

        private void OnDialogClosed(ChildWindowDecorator window)
        {
            FolderBrowserDialog dialog = window.Parent as FolderBrowserDialog;

            if (!dialog.IsOk)
                return;

            Host.MediaService.ImportMediaDatas(null, dialog.SelectedPath);
        }

        #endregion

        #region Burn CD

        void btnBurn_Click(object sender, RoutedEventArgs e)
        {
            BurnSelection();
        }

        private void BurnSelection()
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            List<MediaData> medias = new List<MediaData>();
            foreach (object dataRecord in xdgMedias.SelectedItems)
                medias.Add(dataRecord as MediaData);

            BurnDialog dialog = new BurnDialog(medias);
            dialog.ShowDialog((Desktop)Host.ApplicationDesktop);
        }

        #endregion

        #region Copy to...

        void btnCopyTo_Click(object sender, RoutedEventArgs e)
        {
            if (xdgMedias.SelectedItems.Count < 1)
                return;

            List<MediaData> medias = new List<MediaData>();
            foreach (object dataRecord in xdgMedias.SelectedItems)
                medias.Add(dataRecord as MediaData);

            CopyToDialog dialog = new CopyToDialog(medias);
            dialog.ShowDialog((Desktop)Host.ApplicationDesktop);
        }

        #endregion

        #region Helpers

        /// <summary>
        /// This method allow to display a small icon near the current playing song.
        /// </summary>
        private void SetCurrentMusic(Music newMusic)
        {
            if (_currentMusic != null)
                _currentMusic.IsPlaying = false;

            _currentMusic = newMusic;
            if (_currentMusic != null)
                _currentMusic.IsPlaying = true;
        }

        /// <summary>
        /// Returns true if the mouse cursor is over a scrollbar in the xamDataGrid.
        /// </summary>
        private bool IsMouseOverInvalidZone
        {
            get
            {
                Point ptMouse = MouseUtilities.GetMousePosition(xdgMedias);
                HitTestResult res = VisualTreeHelper.HitTest(xdgMedias, ptMouse);
                if (res == null)
                    return false;

                DependencyObject depObj = res.VisualHit;
                while (depObj != null)
                {
                    if (depObj is XamDataGrid)
                        return false;

                    if (depObj is ScrollBar)
                        return true;

                    //if (depObj is GridViewColumnHeader)
                    //    return true;

                    // VisualTreeHelper works with objects of type Visual or Visual3D.
                    // If the current object is not derived from Visual or Visual3D,
                    // then use the LogicalTreeHelper to find the parent element.
                    if (depObj is System.Windows.Media.Visual || depObj is System.Windows.Media.Media3D.Visual3D)
                        depObj = VisualTreeHelper.GetParent(depObj);
                    else
                        depObj = LogicalTreeHelper.GetParent(depObj);
                }

                return false;
            }
        }

        #endregion

        #region RequestCurrentMusic

        public Music RequestCurrentMusic()
        {
            Music music = null;

            if (Dispatcher.CheckAccess())
                music = AsyncRequestCurrentMusic();
            else
                Dispatcher.Invoke(DispatcherPriority.Send, (DelegateInvoker)delegate { music = AsyncRequestCurrentMusic(); });

            return music;
        }

        private Music AsyncRequestCurrentMusic()
        {
            int newIndex = -1;

            if (xdgMedias.SelectedItems.Count > 0)
                newIndex = xdgMedias.SelectedIndex;
            else if (xdgMedias.Items.Count > 0)
                newIndex = 0;

            // Repeat
            if (newIndex >= xdgMedias.Items.Count)
                newIndex = 0;

            // Shuffle
            if (Host.MediaService.IsShuffleMode)
                newIndex = _shuffleRandom.Next(xdgMedias.Items.Count - 1);

            if (newIndex < 0)
                return null;

            return xdgMedias.Items[newIndex] as Music;
        }

        #endregion

        #region RequestNextMusic

        public Music RequestNextMusic()
        {
            Music music = null;

            if (Dispatcher.CheckAccess())
                music = AsyncRequestNextMusic();
            else
                Dispatcher.Invoke(DispatcherPriority.Send, (DelegateInvoker)delegate { music = AsyncRequestNextMusic(); });

            return music;
        }

        private Music AsyncRequestNextMusic()
        {
            if (xdgMedias.Items.Count < 1)
                return null;

            //---- Get the current index
            int currentIndex = -1; // When no selection, use first one
            if (xdgMedias.SelectedItems.Count > 0)
                currentIndex = xdgMedias.SelectedIndex;

            //---- Shuffle
            if (Host.MediaService.IsShuffleMode)
            {
                int newIndexShuffle = -1;

                do
                {
                    newIndexShuffle = _shuffleRandom.Next(xdgMedias.Items.Count);
                } while (xdgMedias.Items.Count > 1 && currentIndex == newIndexShuffle);

                return xdgMedias.Items[newIndexShuffle] as Music;
            }

            //---- Next
            int newIndex = currentIndex + 1;

            //---- Repeat
            if (newIndex >= xdgMedias.Items.Count)
            {
                if (Host.MediaService.IsRepeatMode)
                    newIndex = 0;
                else
                    return null;
            }

            return xdgMedias.Items[newIndex] as Music;
        }

        #endregion

        #region RequestPreviousMusic

        public Music RequestPreviousMusic()
        {
            Music music = null;

            if (Dispatcher.CheckAccess())
                music = AsyncRequestPreviousMusic();
            else
                Dispatcher.Invoke(DispatcherPriority.Send, (DelegateInvoker)delegate { music = AsyncRequestPreviousMusic(); });

            return music;
        }

        private Music AsyncRequestPreviousMusic()
        {
            if (xdgMedias.Items.Count < 1)
                return null;

            //---- Get the current index
            int currentIndex = -1; // When no selection, use first one
            if (xdgMedias.SelectedItems.Count > 0)
                currentIndex = xdgMedias.SelectedIndex;

            //---- Shuffle
            if (Host.MediaService.IsShuffleMode)
            {
                int newIndexShuffle = -1;

                do
                {
                    newIndexShuffle = _shuffleRandom.Next(xdgMedias.Items.Count);
                } while (xdgMedias.Items.Count > 1 && currentIndex == newIndexShuffle);

                return xdgMedias.Items[newIndexShuffle] as Music;
            }

            //---- Previous
            int newIndex = currentIndex - 1;

            //---- Repeat
            if (newIndex < 0)
            {
                if (Host.MediaService.IsRepeatMode)
                    newIndex = xdgMedias.Items.Count - 1;
                else
                    return null;
            }

            return xdgMedias.Items[newIndex] as Music;
        }

        #endregion

        #region Host : HostCommand

        void Host_HostCommand(object sender, string commandId, params object[] parameters)
        {
            if (Host.VisualService.VisualPartMode != VisualPartMode.Normal)
                return;

            if (commandId == HostCommandIds.PlayMediaData && parameters[0] is Music)
            {
                //---- Activate the right device
                MediaDevice device = Host.MediaService.FindDeviceByHandle("MyLibrary");
                MediaFeature feature = device.FindMediaFeature("Music");

                Host.MediaService.ActivateMediaDevice(device, feature, null);

                //---- Import the file
                Music media = (Music)parameters[0];
                if (feature.CanImport(null, media))
                    feature.AddMediaData(null, media, false);

                //---- Save the changes
                Host.MediaService.CurrentMediaDevice.OnSaveChanges();
                Host.MediaService.NotifyMediaDataOperation(feature, null, MediaOperationKind.Imported, null);

                //---- Play
                Host.MediaService.PlayMedia(media);
            }
        }

        #endregion

    }

    #region FilterSortDescription

    sealed public class FilterSortDescription : System.Collections.IComparer
    {
        public int Compare(object x, object y)
        {
            if (x.Equals("*"))
                return -1;
            return ((string)x).CompareTo((string)y);
        }
    }

    #endregion

    #region MusicTagsCloudManager

    static class MusicTagsCloudManager
    {
        static private TagsCloud _tagsCloud;
        static private ObservableCollection<MediaData> _medias;
        static private Thread _worker;
        static private bool _stop = false;

        static private object LOCK = new object();

        static public void Refresh(TagsCloud cloud, ObservableCollection<MediaData> medias)
        {
            lock (LOCK)
            {
                _tagsCloud = cloud;
                _medias = medias;
                if (_medias == null)
                    Console.WriteLine("ERROR :-P");

                //---- Stop the current one
                _stop = true;
                while (_worker != null)
                    Thread.Sleep(25);

                _worker = null;
                _stop = false;

                //---- Restart
                _worker = new Thread(new ThreadStart(AsyncProcess));
                _worker.Priority = ThreadPriority.Lowest;
                _worker.IsBackground = true;
                _worker.Start();
            }
        }

        static private void AsyncProcess()
        {
            try
            {

                _tagsCloud.Dispatcher.Invoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate
                        {
                            _tagsCloud.Collection.Clear();
                        });

                List<MediaData> toAdd = new List<MediaData>();
                foreach (MediaData media in _medias)
                {
                    toAdd.Add(media);

                    if (toAdd.Count > 30)
                    {
                        _tagsCloud.Dispatcher.Invoke(DispatcherPriority.Normal,
                                    (DelegateInvoker)delegate
                                    {
                                        foreach (MediaData mediaToAdd in toAdd)
                                        {
                                            _tagsCloud.Collection.Add(mediaToAdd);
                                            if (_stop)
                                                return;
                                        }
                                    });

                        toAdd.Clear();
                        Thread.Sleep(100);
                    }

                    if (_stop)
                        return;
                }

                //---- Add the remainder
                _tagsCloud.Dispatcher.Invoke(DispatcherPriority.Normal,
                (DelegateInvoker)delegate
                {
                    foreach (MediaData mediaToAdd in toAdd)
                    {
                        _tagsCloud.Collection.Add(mediaToAdd);
                        if (_stop)
                            return;
                    }
                });

            }
            finally
            {
                _worker = null;
            }
        }
    }

    #endregion

    #region EnumerableWrapperForInfragisticsBugFix

    // Here's the EnumerableWrapper implementation which just a wrapper to another enumerable
    //sealed public class EnumerableWrapperForInfragisticsBugFix : IEnumerable
    //{
    //    public IEnumerable Enumerable;

    //    public EnumerableWrapperForInfragisticsBugFix(IEnumerable enumerable)
    //    {
    //        Enumerable = enumerable;
    //    }

    //    public IEnumerator GetEnumerator()
    //    {
    //        return Enumerable.GetEnumerator();
    //    }
    //}

    #endregion

}